/******************************************************
*This is Intermediate Code generated from PCAT code   *
*Group Member: Nguyen Pham Xuan Quynh   0712717       *
*              Luong Thanh Anh Duc      0712731       *
*Class 07CTT FIT University of Science                *
*Instructor: Vu Quoc Hoang FIT HCMUS                  *
******************************************************/


import java.util.*;

public class Compiler
{
	
	static Integer strCount = 0;
	static Integer stackAlloc = 0;
	static Integer argAlloc = 0;
	static String procname = "";
	
	
	static Integer numTemp = 0;
	static Integer numVar = 0;
	
	static void compile(Ast.Program p)
	{
		
		
		printWelcome();
		ArrayList IRinsList = new ArrayList();
		IRinsList = IRGen.gen(p);
		
		System.out.println("\t.data");
		System.out.println("boundMessage:	.asciiz	\"Boundary Exception\"");
		System.out.println("nullMessage:	.asciiz	\"Null Pointer Exception\"");
		System.out.println("zeroDivMessage:	.asciiz	\"Divide Zero Exception\"");
		
		IRGen.Instruction ins;
		for (int i = 0; i < IRinsList.size(); ++i)
		{
			ins = (IRGen.Instruction)IRinsList.get(i);
			if (ins.ins.equals("proc"))
			{
				genProcDec(ins);
			}
			else if (ins.ins.equals("proci"))
			{
				genProcSetup(ins);
			}
			else if (ins.ins.equals("eproc"))
			{
				genEndProc();
			}
			else if (ins.ins.equals("write"))
			{
				genWrite(ins);
			}
			else if (ins.ins.equals("read"))
			{
				genRead(ins);
			}
			else if (ins.ins.equals("move"))
			{
				genMove(ins);
			}
			else if (ins.ins.equals("add"))
			{
				genAdd(ins);
			}
			else if (ins.ins.equals("sub"))
			{
				genSubtract(ins);
			}
			else if (ins.ins.equals("mult"))
			{
				genMult(ins);
			}
			else if (ins.ins.equals("div"))
			{
				genDiv(ins);
			}
			else if (ins.ins.equals("mod"))
			{
				genMod(ins);
			}
			else if (ins.ins.equals("gt"))
			{
				genGt(ins);
			}
			else if (ins.ins.equals("gte"))
			{
				genGte(ins);
			}
			else if (ins.ins.equals("lt"))
			{
				genLt(ins);
			}
			else if (ins.ins.equals("lte"))
			{
				genLte(ins);
			}
			else if (ins.ins.equals("eq"))
			{
				genEq(ins);
			}
			else if (ins.ins.equals("neq"))
			{
				genNeq(ins);
			}
			else if (ins.ins.equals("comp"))
			{
				genNeg(ins);
			}
			else if (ins.ins.equals("not"))
			{
				genNot(ins);
			}
			else if (ins.ins.equals("jump"))
			{
				genJump(ins);
			}
			else if (ins.ins.equals("jt"))
			{
				genJifTrue(ins);
			}
			else if (ins.ins.equals("jf"))
			{
				genJifFalse(ins);
			}
			else if (ins.ins.equals("jeq"))
			{
				genJeq(ins);
			}
			else if (ins.ins.equals("jneq"))
			{
				genJneq(ins);
			}
			else if (ins.ins.equals("jlte"))
			{
				genJlte(ins);
			}
			else if (ins.ins.equals("jlt"))
			{
				genJlt(ins);
			}
			else if (ins.ins.equals("arg"))
			{
				genArgSetup(ins);
			}
			else if (ins.ins.equals("call"))
			{
				genCall(ins);
			}
			else if (ins.ins.equals("callf"))
			{
				genCallf(ins);
			}
			else if (ins.ins.equals("ret"))
			{
				genRet(ins);
			}
			else if (ins.ins.equals("retf"))
			{
				genRetf(ins);
			}
			else if (ins.ins.equals("arra"))
			{
				genArrAlloc(ins);
			}
			else if (ins.ins.equals("arrg"))
			{
				genArrGet(ins);
			}
			else if (ins.ins.equals("arrs"))
			{
				genArrSet(ins);
			}
			else if (ins.ins.equals("reca"))
			{
				genRecAlloc(ins);
			}
			else if (ins.ins.equals("recg"))
			{
				genRecGet(ins);
			}
			else if (ins.ins.equals("recs"))
			{
				genRecSet(ins);
			}
			else if (ins.ins.equals("entry"))
			{
				genEntry();
			}
			else if (ins.ins.equals("str"))
			{
				genStrDec(ins);
			}
			else if (ins.ins.charAt(0) == '~')
			{
				genLabel(ins);
			}
			
		}
		genExceptionHandler();
		
	}
	static class CompilerError extends Ast.Error 
	{
		CompilerError(int line, String text) 
		{
			super("Error at line " + line + ": " + text);
		}
	}
	static void printWelcome()
	{
		System.out.println("#######################################################");
		System.out.println("#This is Intermediate Code generated from PCAT code   #");
		System.out.println("#Group Member: Luong Thanh Anh Duc      0712731       #");
		System.out.println("#              Nguyen Pham Xuan Quynh   0712717       #");
		System.out.println("#Class 07CTT FIT University of Science                #");
		System.out.println("#Instructor: Vu Quoc Hoang FIT HCMUS                  #");
		System.out.println("#######################################################");
		System.out.println();
	}
	
	static void genStrDec(IRGen.Instruction ins)
	{	
		System.out.println("s" + strCount.toString() + ":	.asciiz " + ins.op1);
		strCount++;
	}
	static void genEntry()
	{
		System.out.println("\t.globl	main_0");
		System.out.println("\t.text");
		
		
	}
	static void genProcDec(IRGen.Instruction ins)
	{
		System.out.println(ins.op1 + ":");
		procname = ins.op1;
		if (ins.op1.equals("main_0"))
		{
			// create static link array
			System.out.println("\tli		$a0, 400");
			System.out.println("\tli		$v0, 9");
			System.out.println("\tsyscall");
			System.out.println("\tmove	$t8, $v0");
		}
	}
	static void genProcSetup(IRGen.Instruction ins)
	{
		numVar = Integer.parseInt(ins.op2);
		numTemp = Integer.parseInt(ins.op3);
		Integer index = Integer.parseInt(procname.substring(procname.indexOf("_") + 1));
		
		stackAlloc = (numVar + numTemp) * 4;
		
		if (stackAlloc != 0)
			System.out.println("\taddi	$sp, $sp, -" + stackAlloc.toString());
		System.out.println("\tmove	$fp, $sp");
	
		
		System.out.println("\tli		$t0, " + index.toString());
		System.out.println("\tsll		$t0, $t0, 2");
		System.out.println("\tadd		$t0, $t0, $t8");
		System.out.println("\tsw		$fp, 0($t0)");
		
	}
	static void genEndProc()
	{
		if (stackAlloc != 0)
			System.out.println("\taddi	$sp, $sp, " + stackAlloc.toString());
		if (procname.equals("main_0"))
		{
			System.out.println("\tli		$v0, 10");
			System.out.println("\tsyscall");
		}
		else
		{
			System.out.println("\tjr		$ra");
		}
	}
	static void genWrite(IRGen.Instruction ins)
	{
		Integer offset = 0;
		String tempVar = "";
		Integer order = 0;
		if (ins.op1.charAt(0) == '?')
		{
			tempVar = ins.op1.substring(1);
			System.out.println("\tli		$v0, 4");
			System.out.println("\tla		$a0, s" + tempVar);
			System.out.println("\tsyscall");
		}
		else 
		{
			getValue(ins.op1);
			System.out.println("\tmove	$a0, $t0");
			System.out.println("\tli		$v0, 1");
			System.out.println("\tsyscall");
		}
	}
	static void genRead(IRGen.Instruction ins)
	{
		String temp = ins.op1.substring(1);
		Integer order = Integer.parseInt(temp);
		Integer offset = numVar * 4 + order * 4;
		
		System.out.println("\tli		$v0, 5");
		System.out.println("\tsyscall");
		System.out.println("\tsw		$v0, " + offset.toString() + "($fp)");
	}
	static void genMove(IRGen.Instruction ins)
	{
		getValue(ins.op2);
		setValue(ins.op1);
	}
	static void genAdd(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tadd		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genSubtract(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tsub		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genMult(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tmult	$t0, $t3");
		System.out.println("\tmflo	$t0");
		setValue(ins.op1);
	}
	static void genDiv(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tbeq		$t3, $zero, zeroExcpHdl");
		System.out.println("\tdiv		$t0, $t3");
		System.out.println("\tmflo	$t0");
		setValue(ins.op1);
	}
	static void genMod(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tbeq		$t3, $zero, zeroExcpHdl");
		System.out.println("\tdiv		$t0, $t3");
		System.out.println("\tmfhi	$t0");
		setValue(ins.op1);
	}
	static void genGt(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tsgt		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genGte(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tsge		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genLt(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tslt		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genLte(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tsle		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genEq(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tseq		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genNeq(IRGen.Instruction ins)
	{
		getValue(ins.op3);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op2);
		System.out.println("\tsne		$t0, $t0, $t3");
		setValue(ins.op1);
	}
	static void genNeg(IRGen.Instruction ins)
	{
		getValue(ins.op2);
		System.out.println("\tneg		$t0, $t0");
		setValue(ins.op1);
	}
	static void genNot(IRGen.Instruction ins)
	{		
		getValue(ins.op2);
		System.out.println("\tli		$t1, 1");
		System.out.println("\tsub		$t0, $t1, $t0");
		setValue(ins.op1);
	}
	static void genLabel(IRGen.Instruction ins)
	{
		String temp = ins.ins.substring(1);
		System.out.println("L" + temp);
	}
	static void genJump(IRGen.Instruction ins)
	{
		String label = "L" + ins.op1.substring(1);
		System.out.println("\tj		" + label);
	}
	static void genJlt(IRGen.Instruction ins)
	{
		String label = "L" + ins.op3.substring(1);
		getValue(ins.op2);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op1);
		System.out.println("\tblt		$t0, $t3, " + label);
	}
	static void genJlte(IRGen.Instruction ins)
	{
		String label = "L" + ins.op3.substring(1);
		getValue(ins.op2);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op1);
		System.out.println("\tble		$t0, $t3, " + label);
	}
	static void genJeq(IRGen.Instruction ins)
	{
		String label = "L" + ins.op3.substring(1);
		getValue(ins.op2);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op1);
		System.out.println("\tbeq		$t0, $t3, " + label);
	}
	static void genJneq(IRGen.Instruction ins)
	{
		String label = "L" + ins.op3.substring(1);
		getValue(ins.op2);
		System.out.println("\tmove	$t3, $t0");
		getValue(ins.op1);
		System.out.println("\tbne		$t0, $t3, " + label);
	}
	static void genJifTrue(IRGen.Instruction ins)
	{
		String label = "L" + ins.op2.substring(1);
		getValue(ins.op1);
		System.out.println("\tbne		$t0, $zero, " + label);
	}
	static void genJifFalse(IRGen.Instruction ins)
	{
		String label = "L" + ins.op2.substring(1);
		getValue(ins.op1);
		System.out.println("\tbeq		$t0, $zero, " + label);
	}
	static void genArgSetup(IRGen.Instruction ins)
	{
		System.out.println("\taddi	$sp, $sp, -4");
		getValue(ins.op1);
		System.out.println("\tsw		$t0, 0($sp)");
		argAlloc += 4;
	}
	static void genCall(IRGen.Instruction ins)
	{
		System.out.println("\taddi	$sp, $sp, -4");
		System.out.println("\tsw		$ra, 0($sp)");
		
		System.out.println("\taddi	$sp, $sp, -4");
		System.out.println("\tsw		$fp, 0($sp)");
		
		System.out.println("\tjal		" + ins.op1);
		
		System.out.println("\tlw		$fp, 0($sp)");
		System.out.println("\taddi	$sp, $sp, 4" );
		
		System.out.println("\tlw		$ra, 0($sp)");
		System.out.println("\taddi	$sp, $sp, 4");
		
		System.out.println("\taddi	$sp, $sp, " + argAlloc.toString());
		argAlloc = 0;
	}
	static void genCallf(IRGen.Instruction ins)
	{
		System.out.println("\taddi	$sp, $sp, -4");
		System.out.println("\tsw		$ra, 0($sp)");
		
		System.out.println("\taddi	$sp, $sp, -4");
		System.out.println("\tsw		$fp, 0($sp)");
		
		System.out.println("\tjal		" + ins.op2);
		
		System.out.println("\tmove	$t0, $v0");
		
		System.out.println("\tlw		$fp, 0($sp)");
		System.out.println("\taddi	$sp, $sp, 4" );
		
		setValue(ins.op1);		
		
		System.out.println("\tlw		$ra, 0($sp)");
		System.out.println("\taddi	$sp, $sp, 4");
		
		System.out.println("\taddi	$sp, $sp, " + argAlloc.toString());
		argAlloc = 0;
	}
	static void genRet(IRGen.Instruction ins)
	{
		if (stackAlloc != 0)
			System.out.println("\taddi	$sp, $sp, " + stackAlloc.toString());	
		System.out.println("\tjr		$ra");
		
	}
	static void genRetf(IRGen.Instruction ins)
	{
		getValue(ins.op2);
		System.out.println("\tmove	$v0, $t0");
		if (stackAlloc != 0)
			System.out.println("\taddi	$sp, $sp, " + stackAlloc.toString());	
		System.out.println("\tjr		$ra");
	}
	static void genArrAlloc(IRGen.Instruction ins)
	{
		getValue(ins.op2);
		System.out.println("\taddi	$a0, $t0, 1");
		System.out.println("\tsll		$a0, $a0, 2");
		System.out.println("\tli		$v0, 9");
		System.out.println("\tsyscall");
		System.out.println("\tsw		$t0, 0($v0)");
		System.out.println("\tmove	$t0, $v0");
		setValue(ins.op1);
	}
	static void genArrSet(IRGen.Instruction ins)
	{
		getValue(ins.op1);
		System.out.println("\tmove	$t2, $t0");		// t2 = array
		System.out.println("\tlw		$t1, 0($t0)"); 	// t1 = length
		getValue(ins.op2); 	
		System.out.println("\tmove	$t4, $t0");		// t4 = index
		getValue(ins.op3);							// t0 = value
		// if index >= length goto boundException
		System.out.println("\tbge		$t4, $t1, boundExcpHdl");		
		
		System.out.println("\taddi	$t4, $t4, 1");
		System.out.println("\tsll		$t4, $t4, 2");
		System.out.println("\tadd		$t4, $t4, $t2");
		System.out.println("\tsw		$t0, 0($t4)");
	}
	static void genArrGet(IRGen.Instruction ins)
	{
		getValue(ins.op2);
		System.out.println("\tmove	$t2, $t0");		// t2 = array
		System.out.println("\tlw		$t1, 0($t0)"); 	// t1 = length
		getValue(ins.op3); 	
		System.out.println("\tmove	$t4, $t0");		// t4 = index
		// if index >= length goto boundException
		System.out.println("\tbge		$t4, $t1, boundExcpHdl");		
		
		System.out.println("\taddi	$t4, $t4, 1");
		System.out.println("\tsll		$t4, $t4, 2");
		System.out.println("\tadd		$t4, $t4, $t2");
		System.out.println("\tlw		$t0, 0($t4)");
		setValue(ins.op1);
	}
	static void genRecAlloc(IRGen.Instruction ins)
	{
		getValue(ins.op2);
		System.out.println("\tmove	$a0, $t0");
		System.out.println("\tsll		$a0, $a0, 2");
		System.out.println("\tli		$v0, 9");
		System.out.println("\tsyscall");
		System.out.println("\tmove	$t0, $v0");
		setValue(ins.op1);
	}
	static void genRecSet(IRGen.Instruction ins)
	{
		getValue(ins.op1);
		System.out.println("\tmove	$t1, $t0");		// t1 = record
		getValue(ins.op2);
		System.out.println("\tmove	$t2, $t0");		// t2 = index
		getValue(ins.op3);							// t0 = value
		System.out.println("\tsll		$t2, $t2, 2");	// t2 = index
		System.out.println("\tadd		$t2, $t2, $t1");
		System.out.println("\tsw		$t0, 0($t2)");		
	}
	static void genRecGet(IRGen.Instruction ins)
	{
		getValue(ins.op2);
		System.out.println("\tmove	$t1, $t0");		// t1 = record
		System.out.println("\tbeq		$t1, $zero, nullExcpHdl");
		getValue(ins.op3);							// t0 = index
		System.out.println("\tsll		$t0, $t0, 2");	// t2 = index
		System.out.println("\tadd		$t0, $t0, $t1");
		System.out.println("\tlw		$t0, 0($t0)");
		setValue(ins.op1);
	}
	
	// get the value of variable and store at $t0
	static void getValue(String varName)
	{
		Integer offset = 0;
		String tempVar = "";
		Integer order = 0;
		if (varName.charAt(0) == '&')
		{
			tempVar = varName.substring(1);
			order = Integer.parseInt(tempVar);
			offset = 4 * numVar + 4 * order;
			System.out.println("\tlw		$t0, " + offset.toString() + "($fp)");
		}
		else if (varName.charAt(0) == '@')
		{
			Integer index = varName.indexOf("_");
			tempVar = varName.substring(1,index);
			
			order = Integer.parseInt(tempVar);
			offset = 4 * order;
			System.out.println("\tlw		$t0, " + offset.toString() + "($fp)");
		}
		else if (varName.charAt(0) == '$')
		{
			Integer depth = Integer.parseInt(varName.substring(1, varName.indexOf("_")));
			order = Integer.parseInt(varName.substring(varName.indexOf("_") + 1, varName.lastIndexOf("_")));
			order = order * 4;
			Integer cur_proc_order = Integer.parseInt(procname.substring(procname.indexOf("_") + 1));  
			
			for (Integer i = 0; i < depth; ++i)
			{
				cur_proc_order = (Integer)IRGen.staticLink[cur_proc_order];
			}
			if (cur_proc_order != 0)
			{
				System.out.println("\tli		$t1, " + cur_proc_order.toString());
				System.out.println("\tsll		$t1, $t1, 2");
				System.out.println("\tadd		$t1, $t1, $t8");
				System.out.println("\tlw		$t1, 0($t1)");
			}
			else
				System.out.println("\tlw		$t1, 0($t8)");	
			
			System.out.println("\tlw		$t0, " + order.toString() + "($t1)");
		}
		else if (varName.charAt(0) == '%')
		{
			Integer index = varName.indexOf("_");
			tempVar = varName.substring(1,index);
			order = Integer.parseInt(tempVar);
			
			index = procname.indexOf("_");
			tempVar = procname.substring(index + 1);
			index = Integer.parseInt(tempVar);
			
			offset = stackAlloc + 4 * ((Integer)IRGen.numArg.get(index) - order + 1);
			System.out.println("\tlw		$t0, " + offset.toString() + "($fp)");
		}
		else	// varname is a constant
		{
			System.out.println("\tli		$t0, " + varName);
		}
	}
	// store $t0 to variable 
	static void setValue(String varName)
	{
		Integer offset = 0;
		String tempVar = "";
		Integer order = 0;
		if (varName.charAt(0) == '&')
		{
			tempVar = varName.substring(1);
			order = Integer.parseInt(tempVar);
			offset = 4 * numVar + 4 * order;
			System.out.println("\tsw		$t0, " + offset.toString() + "($fp)");
		}
		else if (varName.charAt(0) == '@')
		{
			Integer index = varName.indexOf("_");
			tempVar = varName.substring(1,index);
			
			order = Integer.parseInt(tempVar);
			offset = 4 * order;
			System.out.println("\tsw		$t0, " + offset.toString() + "($fp)");
		}
		else if (varName.charAt(0) == '$')
		{
			Integer depth = Integer.parseInt(varName.substring(1, varName.indexOf("_")));
			order = Integer.parseInt(varName.substring(varName.indexOf("_") + 1, varName.lastIndexOf("_")));
			order = order * 4;
			Integer cur_proc_order = Integer.parseInt(procname.substring(procname.indexOf("_") + 1));  
			
			for (Integer i = 0; i < depth; ++i)
			{
				cur_proc_order = (Integer)IRGen.staticLink[cur_proc_order];
			}
			
			if (cur_proc_order != 0)
			{
				System.out.println("\tli		$t1, " + cur_proc_order.toString());
				System.out.println("\tsll		$t1, $t1, 2");
				System.out.println("\tadd		$t1, $t1, $t8");
				System.out.println("\tlw		$t1, 0($t1)");
			}
			else
				System.out.println("\tlw		$t1, 0($t8)");
			
			
			
			System.out.println("\tsw		$t0, " + order.toString() + "($t1)");
		}
		else if (varName.charAt(0) == '%')
		{
			Integer index = varName.indexOf("_");
			tempVar = varName.substring(1,index);
			order = Integer.parseInt(tempVar);
			
			index = procname.indexOf("_");
			tempVar = procname.substring(index + 1);
			index = Integer.parseInt(tempVar);
			
			offset = stackAlloc + 4 * ((Integer)IRGen.numArg.get(index) - order + 1);
			System.out.println("\tsw		$t0, " + offset.toString() + "($fp)");
		}
	}
	static void genExceptionHandler()
	{
		System.out.println("boundExcpHdl:");
		System.out.println("\tla		$a0, boundMessage");
		System.out.println("\tli		$v0, 4");
		System.out.println("\tsyscall");
		System.out.println("\tb			endExcpHdl");
		System.out.println("nullExcpHdl:");
		System.out.println("\tla		$a0, nullMessage");
		System.out.println("\tli		$v0, 4");
		System.out.println("\tsyscall");
		System.out.println("\tb	endExcpHdl");
		System.out.println("zeroExcpHdl:");
		System.out.println("\tla		$a0, zeroDivMessage");
		System.out.println("\tli		$v0, 4");
		System.out.println("\tsyscall");
		System.out.println("\tb	endExcpHdl");
		System.out.println("endExcpHdl:");
		System.out.println("\tli		$v0, 10");
		System.out.println("\tsyscall");	
	}
}